﻿using System;
using System.Threading;
using System.Linq;
using TradeHelp.Utilities;

namespace TradeHelp.MaterializedObjectViews
{
    /// <summary>
    /// Represents a dynamically changing container of objects.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public abstract class QueryView<T> : View<T>, IDisposable
    {
        private readonly object _subscriptionLock = new object();
        private bool _subscribed;

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        /// <filterpriority>2</filterpriority>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(true);
        }

        /// <summary>
        /// 
        /// </summary>
        ~QueryView()
        {
            Dispose(false);
        }

        /// <summary>
        /// Request to stream data after creation.  All incremental updates are
        /// provided to the callback.  StreamData can have the additional side effect
        /// of instructing the stream to initialize and begin streaming to itself.
        /// </summary>
        /// <param name="callback">delegate to handle incremental data message bursts</param>
        public override void StreamData(StreamMessageDelegate<T> callback)
        {
            Invariant.CheckIsNull(callback, "callback");

            StreamData();
            var data = SubscribeCore(callback);
            ThreadPool.QueueUserWorkItem(state => callback(new ViewMessageBurst<T>(data.Select(d => ViewMessage<T>.CreateAdd(d)).ToList(), data.ViewVersion, true)));
        }

        /// <summary>
        /// Request to unsubscribe client code from recieving further incremental updates
        /// </summary>
        /// <param name="callback"></param>
        public override void Unsubscribe(StreamMessageDelegate<T> callback)
        {
            UnsubscribeCore(callback);
        }

        /// <summary>
        /// Request to stream data after creation
        /// </summary>
        public void StreamData()
        {
            lock (_subscriptionLock)
            {
                if (_subscribed) return;
                _subscribed = true;
            }
            StreamDataCore();
        }

        protected virtual void Dispose(bool disposing) { }

        protected abstract void StreamDataCore();
        
        protected abstract ViewData<T> SubscribeCore(StreamMessageDelegate<T> callback);
        
        protected abstract void UnsubscribeCore(StreamMessageDelegate<T> callback);
    }
}
